﻿using DataMonitor_Tools.Tools.SMTP;
using DataMonitor_Tools.Tools;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Caching.Memory;
using DataMonitorWebApi.Controllers;
using DataMonitorMysql.MappingClass;
using System.IO;

namespace DataMonitorWebApi.Utility
{
    public class FileWatcherHelper
    {
        private readonly ILogger<FileWatcherHelper> _logger;
        private readonly IMemoryCache _memoryCache;//内存缓存
        private readonly IHubContext<SiginalrHub> _hubContext;//signalr
        public FileWatcherHelper(ILogger<FileWatcherHelper> logger, IMemoryCache memoryCache, IHubContext<SiginalrHub> hubContext)
        {
            _logger = logger;
            _memoryCache = memoryCache;
            _hubContext = hubContext;
        }
        public void WatcherStrat(string path, string filter)
        {
            FileSystemWatcher watcher = new FileSystemWatcher();
            _logger.LogWarning("新建了一个监控实例"+watcher.ToString());
            //在原来的路径的基础上添加日期文件夹名称，这样就不用递归去找哪个文件夹下的文件了，指定到日期文件夹
            var nowday= DateTime.Now.ToString("MM_dd");
            var fullpath= Path.Combine(path, nowday);
            Directory.CreateDirectory(fullpath);
            watcher.Path = fullpath;
            watcher.Filter = filter;
            watcher.IncludeSubdirectories = false;
            watcher.Created += new FileSystemEventHandler(OnProcess);
            watcher.Error += new ErrorEventHandler(WatcherError);
            watcher.EnableRaisingEvents = true;
            watcher.InternalBufferSize = 8192;
        }

        private void WatcherError(object sender, ErrorEventArgs e)
        {
            string vepath = ((FileSystemWatcher)sender).Path;//获得实际的监控到文件的路径，因为可能会打开很多监控页面，需要判断是哪个车辆的监控路径下有新增的文件，然后再获取这个车辆号，传送给前端，前端根据车辆号来判断是否需要显示
            string parentPath = Path.GetDirectoryName(vepath);
            var userprop = _memoryCache.Get<dm_props_user>(parentPath);
            if (userprop != null)
            {
                FileSystemWatcher watcher = new FileSystemWatcher();
                watcher.Path = userprop.watchpath;
                watcher.Filter = "*.csv";
                watcher.IncludeSubdirectories = true;
                watcher.Created += new FileSystemEventHandler(OnProcess);
                watcher.Error += new ErrorEventHandler(WatcherError);
                watcher.EnableRaisingEvents = true;
            }
            _logger.LogError("监控出现了问题：" + e.GetException().Message + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));


        }

        private void OnProcess(object source, FileSystemEventArgs e)
        {
            if (e.ChangeType == WatcherChangeTypes.Created)
            {
                Task.Run(() => OnCreated(source, e));
            }
        }
        private void Waiting(string path)
        {
            try
            {
                FileInfo fi;
                fi = new FileInfo(path);
                long len1, len2;
                len2 = fi.Length;
                do
                {
                    len1 = len2;
                    Thread.Sleep(1000);//等待1秒钟
                    fi.Refresh();//这个语句不能漏了
                    len2 = fi.Length;
                } while (len1 < len2);
            }
            catch (Exception ex)
            {
                _logger.LogError("方法：waiting出现了错误："+ex.Message);
            }
        }

        private async Task OnCreated(object source, FileSystemEventArgs e)
        {
            try
            {
                var vepath = ((FileSystemWatcher)source).Path;//获得实际的监控到文件的路径，因为可能会打开很多监控页面，需要判断是哪个车辆的监控路径下有新增的文件，然后再获取这个车辆号，传送给前端，前端根据车辆号来判断是否需要显示
                string parentPath = Path.GetDirectoryName(vepath);
                var memname=_memoryCache.Get<string>(vepath + "filename");
                _logger.LogWarning(parentPath+ e.Name);
                var userprop=_memoryCache.Get<dm_props_user>(parentPath);
                var adminprop = _memoryCache.Get<dm_props_admin>("datamonitoradminpara");
                if (userprop!=null && e.Name!=null && adminprop!=null)
                {
                    if(e.Name!=memname)
                    {
                        _memoryCache.Set<string>(vepath + "filename",e.Name);
                        Waiting(e.FullPath);
                        await Task.Run(async () =>
                        {
                            string fileName = System.IO.Path.GetFileName(e.FullPath);

                            //获得csv文件所有的数据
                            var structall = await CSVFileWatch.CSVFileWatchCSVAsyncV2(e.FullPath, fileName, adminprop.reducetimes);
                            switch (structall.status)
                            {
                                case 0:
                                    var lastingtime = adminprop.points / adminprop.sample;//csv文件的持续时间
                                    var spdidx = structall.headers.IndexOf("spd");
                                    List<double> spd = new List<double>();
                                    double spdavg = 0;//平均速度
                                    double sumdistance = 0;//单个文件的里程数
                                    if (spdidx != -1)
                                    {
                                        spd = structall.alldatalist[structall.headers.IndexOf("spd")];
                                        spdavg = await CSVFileWatch.CalSpdAverageAsync(spd);
                                        sumdistance = Math.Round(spdavg / 3600 * lastingtime, 2);
                                    }
                                    var chartall = await CSVFileWatch.CSVFileWatchStructEditAsync(structall, userprop);//图表数据

                                    //获得缓存里的日期，就是监控开始的日期
                                    var datetime = _memoryCache.Get<DateTime>(userprop.watchpath + "datetime");
                                    //获得当前日期
                                    var nowtime = DateTime.Now;
                                    //获得他们之间的日期差，并把这些中间的日期的缓存清除掉
                                    var datespan = DateTimeOperator.GetDateStringsBetweenDates(datetime, nowtime);
                                    for (int i = 0; i < datespan.Length; i++)
                                    {
                                        _memoryCache.Remove(userprop.vehicle + datespan[i] + "sumdistance");
                                        _memoryCache.Remove(userprop.vehicle + datespan[i] + "filecount");
                                    }

                                    //获得缓存里前一次的累计里程，这里需要用到GetOrCreate，因为过了一天日期就不同了，需要重新创建累计里程缓存
                                    var lastsumdis = _memoryCache.GetOrCreate<double>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "sumdistance", value =>
                                    {
                                        return 0;
                                    });
                                    //获得缓存里前一次的文件数目
                                    var filecount = _memoryCache.GetOrCreate<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "filecount", value =>
                                    {
                                        return 0;
                                    });
                                    //设置缓存里这一次的累计里程和文件数
                                    _memoryCache.Set<double>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "sumdistance", lastsumdis + sumdistance);
                                    _memoryCache.Set<int>(userprop.vehicle + DateTime.Now.ToString("MM-dd") + "filecount", filecount + 1);
                                    var sdsent = _memoryCache.Get<double>((userprop.vehicle + DateTime.Now.ToString("MM-dd") + "sumdistance"));
                                    var countsent = _memoryCache.Get<int>((userprop.vehicle + DateTime.Now.ToString("MM-dd") + "filecount"));
                                    #region 开始发送数据
                                    foreach (var i in SiginalrHub.user)
                                    {
                                        await _hubContext.Clients.Group(i).SendAsync("csvfileinfo", chartall, structall.name, spdavg, sdsent, countsent, userprop.watchpath);
                                    }
                                    //创建异常邮件内容
                                    var res = SMTPWarnContent.CreateWarnContent(structall, userprop.vehicle, adminprop.accerrorvalue, adminprop.diserrorvalue);
                                    //先判断有没有异常，没有就不用费劲去看字典了
                                    if (res != "")
                                    {
                                        //先获得mailconfig缓存
                                        var mailconfig = _memoryCache.Get<SMTPConfig>(userprop.vehicle + "mailconfig");
                                        if (mailconfig != null)
                                        {
                                            //再从缓存里看有没有mailerror，如果没有说明第一次碰到error
                                            var firstmailerror = _memoryCache.Get<string>(userprop.vehicle + "mailerror");
                                            if (firstmailerror == null)
                                            {
                                                //第一次碰到错误发送邮件
                                                await SMTPMail.SendEmailAsync(mailconfig, res);
                                                //如果第一次碰到，则创建缓存并设置为第一次的错误信息
                                                var mailerror = _memoryCache.GetOrCreate<string>(userprop.vehicle + "mailerror", value =>
                                                {
                                                    return res;
                                                });
                                                //再创建缓存记录第一次发送的时间
                                                var starttime = _memoryCache.GetOrCreate<DateTime>(userprop.vehicle + "sendtime", value =>
                                                {
                                                    return DateTime.Now;
                                                });
                                            }
                                            //说明缓存里已经有mailerror了，不是第一次出现问题了
                                            else
                                            {
                                                //判断当前的与上一次的是否问题一致，不一致再发邮件，再覆盖记录发送时间
                                                if (res != firstmailerror)
                                                {
                                                    _memoryCache.Set<string>(userprop.vehicle + "mailerror", res);
                                                    await SMTPMail.SendEmailAsync(mailconfig, res);
                                                    _memoryCache.Set<DateTime>(userprop.vehicle + "sendtime", DateTime.Now);
                                                }
                                                //如果一致，则判断是否经过了1小时，然后再发送邮件提醒
                                                else
                                                {
                                                    //先获取上一次缓存里的发送时间
                                                    var lastsendtime = _memoryCache.Get<DateTime>(userprop.vehicle + "sendtime");
                                                    if (SMTPWarnContent.IsPastOneHour(lastsendtime, mailconfig.emailtimespan))
                                                    {
                                                        await SMTPMail.SendEmailAsync(mailconfig, res);
                                                        _memoryCache.Set<DateTime>(userprop.vehicle + "sendtime", DateTime.Now);
                                                    }
                                                }
                                            }
                                        }
                                    }
                                    #endregion
                                    break;
                                case 1:
                                    _logger.LogWarning($"{userprop.vehicle}的{e.Name}文件有空值或nan");
                                    break;
                                case 2:
                                    _logger.LogWarning($"{userprop.vehicle}的{e.Name}文件大小为0kb");
                                    break;
                                case 3:
                                    _logger.LogWarning($"{userprop.vehicle}的{e.Name}文件被占用");
                                    break;
                                case 9:
                                    _logger.LogWarning($"{userprop.vehicle}的{e.Name}文件读取错误，请查看错误日志");
                                    break;
                            }
                        });
                    }
                  
                  
                }
                else
                {
                    _logger.LogWarning("方法：OnCreated出现了问题：由于参数为空读取不到文件"+ parentPath);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
            }
        }


    }
}
